(function(){
    const  version =  1;
    var getVersion = function(){
       return new Date().getTime();
    }
    /**
     * databaseName,
     * database
     * @param {*} option 
     */
    var DBS = function(option){
        this.databaseName = "DBSV2"//getRandomDBName();
        this.version = getVersion();
        this.tableName = "frameLibrary";
        this.database = null;
        this.isInited = false;
        this.callback = null;
        this.writeID = 0;
        this.writeOffset = 0
        this.readOffset = 0;
        this.isReading = false;
        for(var key in option){
            this[key] = option[key];
        }
        this._initDatabase();
        
    }

    var _initDatabase = function(){
        if(typeof indexedDB == 'undefined'){
            console.log('IndexedDB is not supported');
            return ;
        }
        const request = indexedDB.open(this.databaseName,this.version);
        var self = this;
        request.onerror = function(e) {
            self.callback && self.callback.onInitFailed();
            console.error('Unable to open database.',e);
        }
        request.onsuccess = function(e) {
            self.database = e.target.result;
            self.callback && self.callback.onInitSuccess(e);
            console.log('db opened');
        }
        request.onupgradeneeded = function(e) {
            const db = e.target.result;
            if(db.objectStoreNames.contains(self.tableName)){
                db.deleteObjectStore(self.tableName);
            }
            db.createObjectStore(self.tableName,{keyPath:'id',autoIncrement: false});            
            self.isInited = true;
            console.info("onupgradeneeded")
        }
    }

    
    var _clearTable = function(db){
        db = this.database || db;
        var request = db.transaction([this.tableName],"readwrite")
        .objectStore(this.tableName).clear();
        request.onsuccess = function () {
            console.log('clear table success')
        }
        request.onerror = function () {
            console.log('clear table failed')
        }
    }

    var _release = function(){
        if(this.database == null)return ;
        //this.clearTable();
        this.database.objectStoreNames.contains(this.tableName) && this.database.deleteObjectStore(this.tableName);
        this.database.close();
    }
    /**
     * 
     * @param {
        * id:id, 
        * isAudio:isAudio,
        * pts:pts,
        * index:index,
        * data:data
     * } data 
     */
    var writeData = function(data){
        var request = this.database.transaction([this.tableName],"readwrite").objectStore(this.tableName).put(data);
        var self = this;
        request.onerror = function(e) {
            self.callback && self.callback.onWriteFailed(e);
        }
        request.onsuccess = function(e) {
            self.callback && self.callback.onWriteSuccess(e);
        }
    }

    var deleteData = function(id){
        var request = this.database.transaction([this.tableName],"readwrite").objectStore(this.tableName).delete(id);
        var self = this;
        request.onsuccess = function(e){
            console.info('delete ok');
            self.callback && self.callback.onDeleteSuccess(e);
        };
        request.onerror = function(e){
            console.info("delete failed");
            self.callback && self.callback.onDeleteFailed(e);
        };
    }

    var _deleteTable = function(){
        if(this.database == null) return ;
        if(this.database.objectStoreNames.contains(this.tableName)){
            this.database.deleteObjectStore(this.tableName)
        }
        this.database.close();
        console.info("delete table...");
    }
    /**
     * Not fully tested
     * @param {*} id 
     */
    var shiftData = function(id){
        var store = this.database.transaction(this.tableName,"readwrite").objectStore(this.tableName);
        var request = store.indexedDB("id").openCursor(IDBKeyRange.only(id));
        var self = this;
        request.onsuccess = function(event){
            var cursor = event.target.result;
            if(cursor){
                var request = cursor.delete();
                request.onsuccess = function(e){
                    self.callback && self.callback.onShiftSuccess(e);
                };
                request.onerror = function(e){
                    self.callback && self.callback.onShiftFailed(e);
                }
                cursor.continue();
            }
        }
        request.onerror = function(event){
            var self = this;
            console.error("fetchAndDeleteData request failed")
            self.callback && self.callback.onShiftFailed(event);
        }
    }

    var closeDB = function(){
        if(this.database!=null){
            this.database.close();
        }
        console.info("close database");
    }

    var _releaseDB = function(){
        this.close();
        var dbName = self.databaseName
        if(typeof indexedDB == 'undefined'){
            console.log('IndexedDB is not supported');
            return ;
        }
        const request = indexedDB.deleteDatabase(dbName);
        request.onerror = function(e){
            console.info("release database failed")
        };
        request.onsuccess = function(e){
            console.info("release database successfully")
        };
    }

    var batchWriteData = function(data){
        var transaction = this.database.transaction([this.tableName], 'readwrite');
        var objectStore = transaction.objectStore(this.tableName);
        var lastID = -1;
        
        for(var i=0;i<data.length;i++){
            data[i]['id'] = this.writeID;
            objectStore.put(data[i]);
            lastID = this.writeID;
            this.writeID ++;
        }
        var self = this;
        transaction.oncomplete = function(e) {
            self.writeOffset = lastID;
            self.callback && self.callback.onWriteSuccess(e);
        };
        
        transaction.onerror = function(e) {
            self.callback && self.callback.onWriteFailed(e);
        };
    }

    var readData = function(){
        if(this.isReading) return ;
        this.isReading = true;
        const trans = this.database.transaction([this.tableName],'readonly');
        const request = trans.objectStore(this.tableName).get(this.readOffset);
        var self = this;
        request.onerror = function(e) {
            self.isReading = false;
            self.callback && self.callback.onReadFailed(e);
        };
        request.onsuccess = function(e) {
            var data = request.result || null;
            if(data != null) self.readOffset += 1;
            self.isReading = false;
            self.callback && self.callback.onReadSuccess(data);
        };
    }


    var methods = {
        _initDatabase:_initDatabase,
        batchWrite:batchWriteData,
        read:readData,
        //write:writeData,
        //clearTable:_clearTable,
        //delete:deleteData,
        //close:closeDB,
        //release:_release,
        //shift:shiftData
    };

    var fn = DBS.prototype;

    for(var key in methods){
        fn[key] = methods[key];   
    }

    var Module = typeof Module != "undefined" ? Module : {};
    //var ENVIRONMENT_IS_WEB = typeof window == "object";
    var ENVIRONMENT_IS_WORKER = typeof importScripts == "function";
    //var ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string";
    if(ENVIRONMENT_IS_WORKER){
        self['DB4'] = DBS;
    }
})();